package cn.zhangfusheng.elasticsearch.template;

import cn.zhangfusheng.elasticsearch.annotation.dsl.mybatis.DslWithMybatis;
import cn.zhangfusheng.elasticsearch.constant.ElasticSearchConstant;
import cn.zhangfusheng.elasticsearch.exception.GlobalSystemException;
import cn.zhangfusheng.elasticsearch.model.page.PageRequest;
import cn.zhangfusheng.elasticsearch.scan.ElasticSearchEntityRepositoryDetail;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.session.Configuration;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;

/**
 * mybatis 查询
 * @author fusheng.zhang
 * @date 2022-02-28 15:37:06
 */
interface TemplateMybatisApi extends Template, ElasticSearchTemplateApi {

    Logger log = LoggerFactory.getLogger(TemplateMybatisApi.class);

    /**
     * 通过 mybatis 的xml 方式解析出 dsl 语句,执行货返回结果
     * @param params         方法参数转换为map后的参数
     * @param routing        routing
     * @param dslWithMybatis DslWithMybatis注解
     * @param configuration  mybatis config
     * @param index          index
     * @return
     */
    default Object runIbatis(
            ElasticSearchEntityRepositoryDetail entityRepositoryDetail, Method method, Object[] args,
            Map<String, Object> params, String routing,
            DslWithMybatis dslWithMybatis, Configuration configuration, String index) {
        try {
            String nameSpace = String.format("%s.%s", dslWithMybatis.nameSpace(), dslWithMybatis.id());
            MappedStatement mappedStatement = configuration.getMappedStatement(nameSpace);
            BoundSql boundSql = mappedStatement.getBoundSql(params);
            params.forEach(boundSql::setAdditionalParameter);
            String sql = boundSql.getSql();
            sql = ElasticSearchConstant.PATTERN.matcher(sql).replaceAll(" ");
            log.debug("dynamic dsl:{}", sql);
            for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
                String property = parameterMapping.getProperty();
                Object value = boundSql.hasAdditionalParameter(property)
                        ? boundSql.getAdditionalParameter(property)
                        : configuration.newMetaObject(params.get(property)).getValue(property);
                if (sql.charAt(sql.indexOf("?") - 1) != '"' && value instanceof String) {
                    value = String.format("\"%s\"", value);
                }
                if (value instanceof Collection) {
                    //noinspection unchecked,rawtypes
                    value = String.format("[\"%s\"]", String.join("\",\"", ((Collection) value)));
                }
                sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(String.valueOf(value)));
            }
            String[] indices = this.analysisIndex(method, args, index);
            SearchRequest searchRequest = new SearchRequest().routing(routing).indices(indices)
                    .source(new SearchSourceBuilder().query(QueryBuilders.wrapperQuery(sql)));
            log.debug("result dsl:{}", sql);

            // 分页参数提取
            Optional<PageRequest> pageRequestOptional =
                    params.values().stream().filter(o -> o instanceof PageRequest).map(o -> (PageRequest) o).findFirst();
            return this.search(entityRepositoryDetail, method, searchRequest, pageRequestOptional.orElse(null));
        } catch (Exception e) {
            throw new GlobalSystemException(e);
        }
    }
}
